import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js'
import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js'
import { OutlinePass } from 'three/examples/jsm/postprocessing/OutlinePass.js'
import { ShaderPass } from 'three/examples/jsm/postprocessing/ShaderPass.js'
import { FXAAShader } from 'three/examples/jsm/shaders/FXAAShader.js'
import { Line2 } from 'three/examples/jsm/lines/Line2'
import { LineGeometry } from 'three/examples/jsm/lines/LineGeometry'
import useCommand from './commands/useCommand'
import { initToolbar, initAttributePanel } from './modules'
import { RoadDraw, Select } from './services'
import { lineMaterial, pointMaterial } from './material'

import { AMBIENT_LIGHT_COLOR, DIRECTIONAL_LIGHT_COLOR, SCALE } from './editor.config.js'
import './modules/toolbar'

export default class Editor {
  constructor(options) {
    this.options = options
    this.element = options.canvasElement || document.body
    this.width = this.element.clientWidth
    this.height = this.element.clientHeight
    /** 编辑器状态 draw:绘制  pan:平移 */
    this.state = 'none';
    
    /** 快捷键注册器 */
    this.useCommand = useCommand

    this.initScene()
    this.initService()
    this.initModule(options)

    // ==============
    let geometry = new LineGeometry()
    this.lineMesh = new Line2(geometry, lineMaterial)
    this.lineMesh.name = '辅助线'
    // ===============

    this.roadGroup = new THREE.Group()
    this.roadGroup.name = '道路分组'
    this.scene.add(this.roadGroup)

    this.update()
    this.addBackgroundPic()

    
  }
  /**
   * @function: 初始化编辑器场景
   */
  initScene() {
    /** 创建场景 */
    this.scene = new THREE.Scene()
    /** 创建相机 */
    const k = this.width / this.height
    this.camera = new THREE.OrthographicCamera(-SCALE * k, SCALE * k, SCALE, -SCALE, 1, 1000)
    this.camera.up.y = 1
    this.target = this.scene.position
    this.camera.lookAt(this.target)
    this.camera.position.set(0, 0, 100);
    /** 创建渲染器 */
    this.renderer = new THREE.WebGLRenderer({
      antialias: true, //开启抗锯齿
    });
    this.renderer.setSize(this.width, this.height)
    this.renderer.setClearColor(0xaaaaaa)
    this.element.appendChild(this.renderer.domElement)
    this.container = this.renderer.domElement
    /** 创建光照 */
    const ambientLight = new THREE.AmbientLight(AMBIENT_LIGHT_COLOR)
    this.scene.add(ambientLight)
    const obj3d = new THREE.Object3D()
    obj3d.position.set(0, 0, 0)
    const directionalLight = new THREE.DirectionalLight(DIRECTIONAL_LIGHT_COLOR, 1)
    directionalLight.position.set(0, 0, 500)
    directionalLight.target = obj3d
    this.scene.add(obj3d)
    this.scene.add(directionalLight)

    // 创建控件对象
    this.controls = new OrbitControls(this.camera, this.renderer.domElement)
    this.controls.addEventListener("change", this.update.bind(this)); //监听鼠标、键盘事件
    this.controls.enableRotate = false

    // 创建一个渲染器通道，场景和相机作为参数
    let renderPass = new RenderPass(this.scene, this.camera);
    // 创建OutlinePass通道,显示外轮廓边框
    this.outlinePass = new OutlinePass(new THREE.Vector2(this.width, this.height), this.scene, this.camera);
    // 后处理完成，设置renderToScreen为true，后处理结果在Canvas画布上显示
    this.outlinePass.renderToScreen = true;
    //设置要显示边框的网格模型
    //交互的时候可以设置一个鼠标事件，点击选中了某个模型，就直接把某个网格模型作为值的元素
    //  OutlinePass.selectedObjects = [mesh];
    //OutlinePass相关属性设置
    this.outlinePass.visibleEdgeColor = new THREE.Color(1, 0, 0);
    this.outlinePass.hiddenEdgeColor = new THREE.Color(0, 1, 0);
    this.outlinePass.edgeGlow = 0.0;
    this.outlinePass.usePatternTexture = false;
    this.outlinePass.edgeThickness = 1.0;
    this.outlinePass.edgeStrength = 6.0;
    this.outlinePass.downSampleRatio = 2;
    this.outlinePass.pulsePeriod = 0;

    // 去掉锯齿
    let FXAAShaderPass = new ShaderPass(FXAAShader);
    FXAAShaderPass.uniforms['resolution'].value.set(1 / this.width, 1 / this.height);
    FXAAShaderPass.renderToScreen = true;

    // 创建后处理对象EffectComposer，WebGL渲染器作为参数
    this.composer = new EffectComposer(this.renderer);
    // 设置renderPass通道
    this.composer.addPass(renderPass);
    // 设置OutlinePass通道
    this.composer.addPass(this.outlinePass);
    // 设置FXAAShaderPass通道
    this.composer.addPass(FXAAShaderPass);
  }
  /**
   * @function: 初始化功能组件
   */
  initModule(options) {
    this.modules = {
      // 初始化菜单栏
      toolbar: new initToolbar({
        container: options.toolbarElement,
        editor: this,
        editData: {}
      }),
      attributePanel: new initAttributePanel({
        container: options.attributePanel,
        editor: this,
        editData: {}
      })
    }
  }
  initService() {
    this.services = {
      road: new RoadDraw(this),
      select: new Select(this),
    }
  }
  /**
   * @function: 更新状态
   * @param {*} state 状态
   */
  setState(state) {
    this.state = state
    this.services.select.dispose()
    console.log('state', state);
    switch (state) {
      case 'none':
       this.services.select.listen()
        break;
      case 'road.draw':
        this.services.road.listen()
        break;
      // case 'road.drawMultipointCurve':
      //  this.services.drawMultipointCurve.listen()
      //   break;
      default:
        break;
    }
  }
  /**
   * @function: 更新场景
   */
  update() {
    this.composer.render(this.scene, this.camera)
  }
  /**
   * @function: 创建辅助坐标系
   * @param {*} mesh 物体/模型
   */
  addAxis(mesh) {
    const axis = new THREE.AxesHelper(300)
    axis.add(mesh)
    this.scene.add(axis)
    this.update()
  }
  /**
   * @function: 自适应窗口
   */
  onResize() {
    this.width = this.element.clientWidth
    this.height = this.element.clientHeight
    const k = this.width / this.height
    this.renderer.setSize(this.width, this.height);

    this.camera.left = -SCALE * k;
    this.camera.right = SCALE * k;
    this.camera.top = SCALE;
    this.camera.bottom = -SCALE;
    this.camera.updateProjectionMatrix();
    this.update()
  }
  /**
   * @function: 设置线条
   * @param {*} attribute 点位数组(二维点位：Vector2[])
   * @return {*}
   */
  setLine(attribute) {
    this.lineMesh.visible = true
    attribute = attribute.reduce((pre, next) => {
      pre.push(next.x, next.y, 0)
      return pre
    }, [])
    this.lineMesh.geometry.dispose()
    let geometry = new LineGeometry()
    geometry.setPositions(attribute)
    this.lineMesh.geometry = geometry
    this.scene.add(this.lineMesh)
    this.update()
  }
  /**
   * @function: 隐藏线条
   */
  hiddenLine() {
    this.lineMesh.visible = false
  }
  /**
   * @function: 添加物体
   * @param {*} mesh 物体/模型
   */
  addMesh(mesh) {
    this.roadGroup.add(mesh)
    this.update()
  }
  /**
   * @function: 移动物体
   * @param {*} mesh 物体/模型
   * @param {*} position 点位坐标 Vector3
   * @return {*}
   */
  moveMesh(mesh, position) {
    mesh.position.copy(position)
  }
  /**
   * @function: 该物体的父对象下的所有物体同步移动
   * @param {*} mesh 物体/模型
   * @param {*} position 点位坐标 Vector3
   * @return {*}
   */
  moveWithNodeMesh(mesh, position) {
    let parent = mesh.parent
    parent.children && parent.children.forEach(mesh => {
      mesh.position.copy(position)
    })
    this.update()
  }
  /**
   * @function: 删除物体
   * @param {*} mesh 物体/模型
   */
  removeMesh(mesh) {
    this.roadGroup.remove(mesh)
    this.update()
  }
  /** 选中物体 */
  /**
  * @function: 选中物体
  * @param {*} selectedMesh 物体/模型
  */
  selectedMesh(selectedMesh) {
    this.outlinePass.selectedObjects = [selectedMesh];
    this.createPoints(selectedMesh)
    this.update()
  }
  /**
   * @function: 创建可以编辑的点
   * @param {*} selectedMesh 物体/模型
   * @return {*}
   */
  createPoints(selectedMesh) {
    let parent = selectedMesh.parent
    if (parent.children.find(v => v.isPoints)) return
    let pointsData = selectedMesh.userData.pointsData
    let pointGeometry = new THREE.BufferGeometry()
    pointGeometry.setFromPoints(pointsData)
    this.point = new THREE.Points(pointGeometry, pointMaterial)
    selectedMesh.parent.add(this.point)
  }
  /**
   * @function: 删除可以编辑的点
   */
  deletePoints() {
    this.point && this.point.parent.remove(this.point)
  }
  /**
   * @function: 取消选中
   * @param {*} selectedMesh 物体/模型
   * @return {*}
   */
  cancelSelected(selectedMesh) {
    this.outlinePass.selectedObjects = [];
    this.deletePoints(selectedMesh)
    this.update()
  }
  addBackgroundPic() {
    new THREE.TextureLoader().load('/static/d.png', (texture) => {
      const k = this.width / this.height
      let plan = new THREE.PlaneBufferGeometry(SCALE * k, SCALE, 10, 10)
      let material = new THREE.MeshBasicMaterial({
        map: texture,
        color: 0xffffff,
        side: THREE.DoubleSide,
        depthTest: false, //不进行深度测试.
      })
      let mesh = new THREE.Mesh(plan, material)
      mesh.renderOrder = -1
      mesh.name = 'Land'
      this.addAxis(mesh)
    })
  }
  /**
   * @function: 根据uuid获取一个对象
   * @param {*} uuid id
   * @return {*}
   */
  objectByUuid(uuid) {
    return this.scene.getObjectByProperty('uuid', uuid, true);
  }
  /**
   * @function: 执行命令
   * @param {*} cmd command命令
   */
  execute(cmd) {
    //首先添加到执行列表中，等待执行
    cmd.editor = this
    this.useCommand.execute(cmd)
  }
  /**
   * @function: 撤销上一个命令
   */
  undo() {
    this.useCommand.undo();
  }
  /**
   * @function: 重做上一个命令
   */
  redo() {
    this.useCommand.redo();
  }
  /**
   * @function: 编辑器销毁时
   */
  destroy() {
    this.useCommand.destroyArray.forEach(fn => fn && fn());
  }
  /**
   * @function: 编辑器注册快捷键命令
   * @param {*} command  { name:'' , keyboard: []|'' , execute:function }
   * @return {*}
   */
  registryCommand(command) {
    this.useCommand.registry(command)
  }
}

